package graphing;

public class FourierUtility {
	/*
	 * NOTE:
	 * 
	 * 		The sweep array nesting goes like this, from top to bottom:
	 * 			?
	 * 			?
	 * 			phase (sin/cos)
	 * 			pitch (12 notes)
	 * 			wavelength (multiples of a frequency)
	 * 			actual values
	 * 
	 */
	
	/*TODO account for sample rate when comparing a certain length segment to the base wavelength.
	 * 			length/samplerate should equal 1/50 seconds and basewavelength should be multiplied 
	 * 				by the length to account for this. ==> this should work now
	 */
	
	
	//Starting from the base note, these are all the covered frequencies for all the octaves
	public static final int[] frequencyMultipliers = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
														18,20,22,24,26,28,30,32,
														36,40,44,48,52,56,60,64,
														72,80,88,96,104,112,120,128,
														144,160,176,192,208,224,240,256};
	
	public static final float stepRatio = 1.059453094f; //equal to 2^(1/12)
	public static final float baseWavelength = 1.528911721f; //in seconds/50, the time-wavelength of C1
															//1/50 of a second is the base b/c of perception limits
	
	//gets a fourier coefficient based on a set/range of data, certain waveform, wavelength, and phase info
	public static float getCoefficient(byte[] data, int start, int length, Waveform form, float wavelength, boolean defaultPhase){
		float val = 0;
		for(int i = start; i < start+length-1; i++){
			if(defaultPhase) val += .5*(data[i]*form.calculateValue(i-start, wavelength)+data[i+1]*form.calculateValue(i-start+1, wavelength));
			else val += .5*(data[i]*form.calculateValue(i-start+wavelength/4, wavelength)+data[i+1]*form.calculateValue(i-start+1+wavelength/4, wavelength));
		}
		return val/length;
	}
	
	//collects an array of coefficients from a sweep of all allowed wavelengths (based on freq. multiplier)
	public static float[] sweepWavelength(byte[] data, int start, int length, Waveform form, float wavelength, boolean defaultPhase){
		float[] vals = new float[frequencyMultipliers.length];
		for(int i = 0; i < vals.length; i++){
			vals[i] = getCoefficient(data, start, length, form, wavelength/frequencyMultipliers[i], defaultPhase);
		}
		return vals;
	}
	
	//collects double array of coef. from a pitch sweep across all 12 "steps"
	public static float[][] sweepPitch(byte[] data, int start, int length, Waveform form, boolean defaultPhase){
		float[][] vals = new float[12][];
		for(int i = 0; i < 12; i++){
			vals[i] = sweepWavelength(data, start, length, form, (float)(baseWavelength*length*Math.pow(stepRatio, i)), defaultPhase);
		}
		return vals;
	}
	
	//collects triple array of coef. from a phase sweep
	public static float[][][] sweepPhase(byte[] data, int start, int length, Waveform form){
		float[][][] vals = new float[2][][];
		vals[0] = sweepPitch(data, start, length, form, true);
		vals[1] = sweepPitch(data, start, length, form, false);
		return vals;
	}
	
	//collects quadruple array of coef. from a waveform sweep
	// goes saw_right, saw_left, triangle, square, sine
	public static float[][][][] sweepForm(byte[] data, int start, int length){
		float[][][][] vals = new float[Waveform.values().length][][][];
		for(int i = 0; i < vals.length; i++){
			vals[i] = sweepPhase(data, start, length, Waveform.values()[i]);
		}
		return vals;
	}
	
	//returns the lcm of two floats within a .01 margin (the value by which b needs to be multiplied)
	public static double lcm(double a, double b){
		double difference = b%a;
		double current = 0;
		double multiplier = 1;
		while(difference > .01){
			current = a/difference;
			multiplier *= current;
			difference = a*(current%1);
		}
		return multiplier;
	}
}
